package ph.com.gs3.formalistics.model.ldao;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import ph.com.gs3.formalistics.global.constants.DocumentFilter;
import ph.com.gs3.formalistics.global.constants.StarMark;
import ph.com.gs3.formalistics.global.utilities.Serializer;
import ph.com.gs3.formalistics.global.utilities.logging.FLLogger;
import ph.com.gs3.formalistics.model.LocalDataAccessObject;
import ph.com.gs3.formalistics.model.tables.DocumentsTable;
import ph.com.gs3.formalistics.model.tables.UserDocumentsTable;
import ph.com.gs3.formalistics.model.vo.application.SearchCondition;
import ph.com.gs3.formalistics.model.vo.business.document.Document;
import ph.com.gs3.formalistics.model.vo.business.document.DocumentAction;
import ph.com.gs3.formalistics.model.vo.business.document.DocumentSummary;
import ph.com.gs3.formalistics.model.vo.business.form.Form;
import ph.com.gs3.formalistics.model.vo.business.form.FormFieldData;
import ph.com.gs3.formalistics.model.vo.parsers.json.DocumentActionJSONParser;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;

public class DocumentsLDAO extends LocalDataAccessObject {

	public DocumentsLDAO(Context context) {
		super(context);
	}

	// =================================================================================
	// {{ Insert & Update Methods

	public Document saveDocument(Document document) throws JSONException {

		// Throws JSONException
		ContentValues cv = createCVFromDocument(document);

		long insertId = database.insert(DocumentsTable.NAME, null, cv);

		if (insertId > 0) {
			return getDocument((int) insertId);
		}

		return null;
	}

	public Document updateDocument(Document document, int userId) throws JSONException {

		// Throws JSONException
		ContentValues cv = createCVFromDocument(document);

		// @formatter:off
		String whereClause 	= DocumentsTable.COL_WEB_ID + " = ? AND " 
							+ DocumentsTable.COL_FORM_ID + " = ?";
		String[] whereArgs 	= { document.getWebId(), Integer.toString(document.getFormId()) };
		// @formatter:on

		long affectedRows = database.update(DocumentsTable.NAME, cv, whereClause, whereArgs);

		if (affectedRows > 1) {
			FLLogger.w(TAG,
			        "More than one documents where updated with webId = " + document.getWebId()
			                + " and formId = " + document.getFormId());
		}

		if (affectedRows == 1) {
			return getDocument(document.getWebId(), document.getFormId(), userId);
		}

		return null;

	}

	public JSONObject insertDocumentFieldValues(int documentId, String tableName,
	        JSONObject fieldValuesJSON, List<FormFieldData> formFields) throws JSONException {

		ContentValues cv = createCVFromJSON(fieldValuesJSON, formFields);

		cv.put("Document_id", documentId);

		long insertId = database.insert(tableName, null, cv);

		if (insertId > 0) {
			// Throws JSONException
			return getDocumentFieldValues((int) insertId, tableName, formFields);
		}

		return null;

	}

	public JSONObject updateDocumentFieldValues(int documentId, String tableName,
	        JSONObject fieldValuesJSON, List<FormFieldData> formFields) throws JSONException {

		ContentValues cv = createCVFromJSON(fieldValuesJSON, formFields);

		String whereClause = "Document_id = ?";
		String[] whereArgs = { Integer.toString(documentId) };

		int affectedRows = database.update(tableName, cv, whereClause, whereArgs);

		if (affectedRows >= 1) {
			// Throws JSONException
			return getDocumentFieldValues(documentId, tableName, formFields);
		}

		return null;

	}

	// }}

	// =================================================================================
	// {{ Get Document Method Variants

	public Document getDocument(String webId, int formId, int userId) throws JSONException {

		// @formatter:off
		String whereClause 	= DocumentsTable.COL_WEB_ID + " = ? AND " 
							+ DocumentsTable.COL_FORM_ID + " = ? AND "
							+ "ud.user_id = ?";
		String[] whereArgs 	= { webId, Integer.toString(formId), Integer.toString(userId) };
		// @formatter:on

		return getDocument(whereClause, whereArgs);
	}

	public Document getDocument(int id) throws JSONException {

		String whereClause = DocumentsTable.COL_ID + " = ?";
		String[] whereArgs = { Integer.toString(id) };

		return getDocument(whereClause, whereArgs);

	}

	public Document getDocument(String whereClause, String[] whereArgs) throws JSONException {

		String query = "SELECT d.*, ud.is_starred FROM Documents d "
		        + "LEFT JOIN User_Documents ud ON d._id = ud.document_id " + "WHERE " + whereClause;

		Document document = null;

		Cursor cursor = database.rawQuery(query, whereArgs);

		cursor.moveToFirst();

		if (!cursor.isAfterLast()) {
			document = cursorToDocument(cursor, false);
		}

		cursor.close();

		return document;

	}

	// }}

	// =================================================================================
	// {{ Get Documents Method Variants

	public List<Document> getUserDocuments(int userId) {

		String whereClause = "ud.user_id = ?";
		String[] whereArgs = { Integer.toString(userId) };

		return getDocuments(whereClause, whereArgs);

	}

	public List<Document> getUserForApprovalDocuments(int userId) {

		String whereClause = "ud.user_id = ? AND d." + DocumentsTable.COL_PROCESSOR + " = ?";
		String[] whereArgs = { Integer.toString(userId), Integer.toString(userId) };

		return getDocuments(whereClause, whereArgs);

	}

	public List<Document> getUserOwnDocuments(int userId) {

		String whereClause = "ud.user_id = ? AND d." + DocumentsTable.COL_AUTHOR + " = ?";
		String[] whereArgs = { Integer.toString(userId), Integer.toString(userId) };

		return getDocuments(whereClause, whereArgs);

	}

	public List<Document> getDocuments(String whereClause, String[] whereArgs) {

		List<Document> documents = new ArrayList<>();

		// @formatter:off
		String query = "SELECT d.*, d.processor_id, ud.is_starred FROM Documents d "
		        + "LEFT JOIN User_Documents ud ON d._id = ud.document_id "
		        + "WHERE " + whereClause + " AND d.parent_document_id = 0 "
		        + "ORDER BY date_updated DESC;";		
		// @formatter:on

		Cursor cursor = database.rawQuery(query, whereArgs);

		cursor.moveToFirst();

		while (!cursor.isAfterLast()) {

			cursor.moveToNext();
		}

		cursor.close();

		return documents;

	}

	// }}

	// =================================================================================
	// {{ Get Document Summaries Method Variants

	public List<DocumentSummary> getUserDocumentSummaries(int userId, int formId,
	        DocumentFilter documentFilter) throws JSONException {

		// TODO: add limiters (from and to) to the results

		String whereClause = "";
		List<String> whereArgsList = new ArrayList<>();
		String[] whereArgs = null;

		if (formId > 0) {
			whereClause = "d.form_id = ?";
			whereArgsList.add(Integer.toString(formId));

			// More filters to come
			if (documentFilter != DocumentFilter.ALL_DOCUMENTS) {
				whereClause += " AND ";
			}

		}

		switch (documentFilter) {
			case ALL_DOCUMENTS:
				break;
			case MY_WORK:
				whereClause += "d.processor_id = ?";
				whereArgsList.add(Integer.toString(userId));
				break;
			case MY_DOCUMENTS:
				whereClause += "d.author_id = ?";
				whereArgsList.add(Integer.toString(userId));
				break;
			case STARRED_DOCUMENTS:
				whereClause += "ud.is_starred = ?";
				whereArgsList.add(Integer.toString(1));
				break;
			case OUTGOING_DOCUMENTS:
				throw new RuntimeException("Cannot get document summaries from outgoing documents.");
		}

		whereArgs = new String[whereArgsList.size()];
		whereArgsList.toArray(whereArgs);
		return getUserDocumentSummaries(userId, whereClause, whereArgs);
	}

	public List<DocumentSummary> getUserForApprovalDocumentSummaries(int userId)
	        throws JSONException {

		// TODO: add limiters (from and to) to the results

		String whereClause = "d.processor_id = ?";
		String[] whereArgs = { Integer.toString(userId) };

		return getUserDocumentSummaries(userId, whereClause, whereArgs);

	}

	public List<DocumentSummary> getUserOwnDocumentSummaries(int userId) throws JSONException {

		// TODO: add limiters (from and to) to the results

		String whereClause = "d.author_id = ?";
		String[] whereArgs = { Integer.toString(userId) };

		return getUserDocumentSummaries(userId, whereClause, whereArgs);

	}

	public List<DocumentSummary> getUserStarredDocumentSummaries(int userId) throws JSONException {

		// TODO: add limiters (from and to) to the results

		String whereClause = "ud.is_starred = ?";
		String[] whereArgs = { "1" };

		return getUserDocumentSummaries(userId, whereClause, whereArgs);

	}

	public List<DocumentSummary> getUserDocumentSummaries(int userId, String whereClause,
	        String[] whereArgs) throws JSONException {

		List<DocumentSummary> documentSummaries = new ArrayList<>();

		if (whereClause != null && !whereClause.isEmpty()) {
			whereClause = "AND " + whereClause;
		}

		// @formatter:off
		String query = "SELECT 	d._id AS document_id, "
						+ "d.tracking_number, "
        				+ "d.status, "
        				+ "f._id AS form_id, "
        				+ "f.name AS form_name, "
        				+ "d.author_id, "
        				+ "d.processor_id, "
        				+ "author.display_name AS author_display_name, "
        				+ "d.processor_id, ud.is_starred, "
        				+ "d.actions, d.field_values, "
        				+ "d.date_updated, "
        				+ "(SELECT COUNT(_id) FROM comments WHERE document_id = d._id) AS comment_count "
    				+ "FROM Documents d	"
        				+ "LEFT JOIN User_Documents ud ON d._id = ud.document_id "
        				+ "LEFT JOIN Forms f ON d.form_id = f._id "
        				+ "LEFT JOIN Users author ON d.author_id = author._id "
    				+ "WHERE ud.user_id='" + userId + "' " + whereClause + " "
    				+ "ORDER BY date_updated DESC";
		// @formatter:on

		Cursor cursor = database.rawQuery(query, whereArgs);
		cursor.moveToFirst();

		while (!cursor.isAfterLast()) {
			documentSummaries.add(cursorToDocumentSummary(cursor));
			cursor.moveToNext();
		}

		cursor.close();

		return documentSummaries;

	}

	// }}

	// =================================================================================
	// {{ Other Query Methods

	public List<JSONObject> search(Form form, List<String> resultFieldNames, int ownerId)
	        throws JSONException {

		return search(form, resultFieldNames, ownerId, null);

	}

	public List<JSONObject> search(Form form, List<String> resultFieldNames, int ownerId,
	        List<SearchCondition> conditions) throws JSONException {

		List<JSONObject> results = new ArrayList<>();

		// @formatter:off
		List<String> defaultFields = new ArrayList<>(
		        Arrays.asList(new String[] 
		        		{ "doc._id, web_id, tracking_number, status, date_created, date_updated" }
		        ));
		// @formatter:on

		// Merge the default fields with the required result field names
		Set<String> resultFieldNamesSet = new HashSet<String>(defaultFields);
		resultFieldNamesSet.addAll(resultFieldNames);

		List<String> mergedResultFields = new ArrayList<>(resultFieldNamesSet);
		String fieldSelectionString = Serializer.serializeList(mergedResultFields);

		// @formatter:off
		String query = "SELECT " + fieldSelectionString + " FROM User_Documents udoc "
				+ "LEFT JOIN " + form.getGeneratedFormTableName() + " fields ON doc._id = fields.document_id "
				+ "LEFT JOIN Documents doc ON doc._id = udoc.document_id "
		        + "WHERE doc.Form_id=? AND udoc.user_id=? AND udoc.is_outgoing=?";
		// @formatter:on

		if (conditions != null) {
			query += " AND " + generateWhereClauseFromConditions(conditions);
		}

		String[] params = { Integer.toString(form.getId()), Integer.toString(ownerId), "0" };

		Cursor cursor = database.rawQuery(query, params);

		cursor.moveToFirst();

		while (!cursor.isAfterLast()) {
			// Throws JSONException
			JSONObject json = cursorToJSONObject(cursor);
			results.add(json);

			cursor.moveToNext();
		}

		cursor.close();

		return results;

	}

	public JSONObject getDocumentFieldValues(int documentId, String tableName,
	        List<FormFieldData> formFields) throws JSONException {
		// Map<String, String> fieldValues = new HashMap<>();

		JSONObject fieldValues = new JSONObject();

		int fieldCount = formFields.size();
		String[] columns = new String[fieldCount];

		for (int i = 0; i < fieldCount; i++) {
			columns[i] = formFields.get(i).getId();
		}

		String whereClause = "Document_id = ?";
		String[] whereArgs = { Integer.toString(documentId) };

		Cursor cursor = database
		        .query(tableName, columns, whereClause, whereArgs, null, null, null);

		cursor.moveToFirst();

		if (!cursor.isAfterLast()) {
			for (FormFieldData formField : formFields) {
				int columnIndex = cursor.getColumnIndexOrThrow(formField.getId());
				fieldValues.put(formField.getId(), cursor.getString(columnIndex));
			}
		}

		cursor.close();

		return fieldValues;

	}

	public List<String> getFormDocumentIdList(int formId) {

		String whereClause = DocumentsTable.COL_FORM_ID + " = ?";
		String[] whereArgs = { Integer.toString(formId) };
		String[] columns = { DocumentsTable.COL_WEB_ID };

		Cursor cursor = database.query(DocumentsTable.NAME, columns, whereClause, whereArgs, null,
		        null, null);

		List<String> idList = new ArrayList<>();

		cursor.moveToFirst();
		while (!cursor.isAfterLast()) {

			int index = cursor.getColumnIndexOrThrow(DocumentsTable.COL_WEB_ID);
			idList.add(cursor.getString(index));

			cursor.moveToNext();
		}

		cursor.close();

		return idList;

	}

	public Map<String, String> getFormDocumentWebIdAndIdPairList(int formId) {

		String whereClause = DocumentsTable.COL_FORM_ID + " = ?";
		String[] whereArgs = { Integer.toString(formId) };
		String[] columns = { DocumentsTable.COL_ID, DocumentsTable.COL_WEB_ID };

		Cursor cursor = database.query(DocumentsTable.NAME, columns, whereClause, whereArgs, null,
		        null, null);

		Map<String, String> documentIdAndWebId = new HashMap<>();

		cursor.moveToFirst();
		while (!cursor.isAfterLast()) {

			int idIndex = cursor.getColumnIndexOrThrow(DocumentsTable.COL_ID);
			int webIdIndex = cursor.getColumnIndexOrThrow(DocumentsTable.COL_WEB_ID);

			String id = cursor.getString(idIndex);
			String webId = cursor.getString(webIdIndex);

			documentIdAndWebId.put(webId, id);

			cursor.moveToNext();
		}

		cursor.close();

		return documentIdAndWebId;

	}

	// }}

	// ========================================================================================
	// {{ Parsers

	protected static ContentValues createCVFromDocument(Document document) throws JSONException {

		ContentValues cv = new ContentValues();

		// Throws JSONException
		JSONArray rawActions = DocumentActionJSONParser
		        .createJSONArrayFromDocumentActionList(document.getActions());

		String rawfieldsRequired = Serializer.serializeList(document.getFieldsRequired());
		String rawfieldsEnabled = Serializer.serializeList(document.getFieldsEnabled());
		String rawfieldsHidden = Serializer.serializeList(document.getFieldsHidden());

		cv.put(DocumentsTable.COL_WEB_ID, document.getWebId());
		cv.put(DocumentsTable.COL_TRACKING_NUMBER, document.getTrackingNumber());

		cv.put(DocumentsTable.COL_FORM_ID, document.getFormId());

		cv.put(DocumentsTable.COL_ACTIONS, rawActions.toString());

		cv.put(DocumentsTable.COL_STATUS, document.getStatus());
		cv.put(DocumentsTable.COL_PROCESSOR, document.getProcessorId());
		cv.put(DocumentsTable.COL_AUTHOR, document.getAuthorId());

		cv.put(DocumentsTable.COL_DATE_CREATED, document.getDateCreated());
		cv.put(DocumentsTable.COL_DATE_UPDATED, document.getDateUpdated());

		cv.put(DocumentsTable.COL_FIELDS_REQUIRED, rawfieldsRequired);
		cv.put(DocumentsTable.COL_FIELDS_ENABLED, rawfieldsEnabled);
		cv.put(DocumentsTable.COL_FIELDS_HIDDEN, rawfieldsHidden);

		cv.put(DocumentsTable.COL_FIELD_VALUES, document.getFieldValuesJSON().toString());
		cv.put(DocumentsTable.COL_COMMENTS_LAST_UPDATE_DATE, document.getCommentsLastUpdateDate());

		return cv;

	}

	protected static ContentValues createCVFromJSON(JSONObject json, List<FormFieldData> formFields) {

		ContentValues cv = new ContentValues();

		for (FormFieldData formField : formFields) {
			String key = formField.getId();
			if (json.has(key)) {
				try {
					cv.put(key, json.getString(key));
				} catch (JSONException e) {
					// This should not happen since we just checked the key
					FLLogger.w(TAG, "Error finding key " + key);
				}
			}
		}

		return cv;
	}

	protected static ContentValues createCVFromMap(Map<String, String> map) {

		ContentValues cv = new ContentValues();

		Iterator<Map.Entry<String, String>> iterator = map.entrySet().iterator();
		while (iterator.hasNext()) {
			Map.Entry<String, String> entry = iterator.next();
			cv.put(entry.getKey(), entry.getValue());
		}

		return cv;

	}

	public static Document cursorToDocument(Cursor cursor, boolean isJoined) throws JSONException {

		// @formatter:off
		String idCol				= DocumentsTable.COL_ID;
		String webIdCol				= DocumentsTable.COL_WEB_ID;
		String trackingNumberCol	= DocumentsTable.COL_TRACKING_NUMBER;
		
		String formIdCol			= DocumentsTable.COL_FORM_ID;
		String actionsCol			= DocumentsTable.COL_ACTIONS;
		
		String statusCol			= DocumentsTable.COL_STATUS;
		String processorIdCol		= DocumentsTable.COL_PROCESSOR;
		String authorIdCol			= DocumentsTable.COL_AUTHOR;
		
		String dateCreatedCol 		= DocumentsTable.COL_DATE_CREATED;
		String dateUpdatedCol 		= DocumentsTable.COL_DATE_UPDATED;
		
		String fieldsEnabledCol 	= DocumentsTable.COL_FIELDS_ENABLED;
		String fieldsRequiredCol 	= DocumentsTable.COL_FIELDS_REQUIRED;
		String fieldsHiddenCol 		= DocumentsTable.COL_FIELDS_HIDDEN;
		
		String fieldValuesCol		= DocumentsTable.COL_FIELD_VALUES;
		String isStarredCol 		= UserDocumentsTable.COL_IS_STARRED;

		String commentsLastUpdateCol	= DocumentsTable.COL_COMMENTS_LAST_UPDATE_DATE;
		// @formatter:on

		if (isJoined) {
			idCol = DocumentsTable.JOIN_PREFIX + idCol;
			webIdCol = DocumentsTable.JOIN_PREFIX + webIdCol;
			trackingNumberCol = DocumentsTable.JOIN_PREFIX + trackingNumberCol;

			formIdCol = DocumentsTable.JOIN_PREFIX + formIdCol;
			actionsCol = DocumentsTable.JOIN_PREFIX + actionsCol;

			statusCol = DocumentsTable.JOIN_PREFIX + statusCol;
			processorIdCol = DocumentsTable.JOIN_PREFIX + processorIdCol;
			authorIdCol = DocumentsTable.JOIN_PREFIX + authorIdCol;

			dateCreatedCol = DocumentsTable.JOIN_PREFIX + dateCreatedCol;
			dateUpdatedCol = DocumentsTable.JOIN_PREFIX + dateUpdatedCol;

			fieldsEnabledCol = DocumentsTable.JOIN_PREFIX + fieldsEnabledCol;
			fieldsRequiredCol = DocumentsTable.JOIN_PREFIX + fieldsRequiredCol;
			fieldsHiddenCol = DocumentsTable.JOIN_PREFIX + fieldsHiddenCol;

			fieldValuesCol = DocumentsTable.JOIN_PREFIX + fieldValuesCol;
			commentsLastUpdateCol = DocumentsTable.JOIN_PREFIX + commentsLastUpdateCol;

		}

		// @formatter:off
		// Initialize indices

		int idIndex 			= cursor.getColumnIndexOrThrow(idCol);
		int webIdIndex 			= cursor.getColumnIndexOrThrow(webIdCol);
		int trackingNumberIndex = cursor.getColumnIndexOrThrow(trackingNumberCol);
		
		int formIdIndex 		= cursor.getColumnIndexOrThrow(formIdCol);
		
		int actionsIndex 		= cursor.getColumnIndexOrThrow(actionsCol);
				
		int statusIndex			= cursor.getColumnIndexOrThrow(statusCol);
		int processorIdIndex 	= cursor.getColumnIndexOrThrow(processorIdCol);
		int authorIdIndex 		= cursor.getColumnIndexOrThrow(authorIdCol);
				
		int dateCreatedIndex	= cursor.getColumnIndexOrThrow(dateCreatedCol);
		int dateUpdatedIndex	= cursor.getColumnIndexOrThrow(dateUpdatedCol);
				
		int fieldsEnabledIndex 	= cursor.getColumnIndexOrThrow(fieldsEnabledCol);
		int fieldsRequiredIndex = cursor.getColumnIndexOrThrow(fieldsRequiredCol);
		int fieldsHiddenIndex 	= cursor.getColumnIndexOrThrow(fieldsHiddenCol);
		
		int fieldsValuesIndex 	= cursor.getColumnIndexOrThrow(fieldValuesCol);				
		int isStarredIndex 		= cursor.getColumnIndexOrThrow(isStarredCol);
		
		int commentsLastUpdateDateIndex = cursor.getColumnIndexOrThrow(commentsLastUpdateCol);
		
		// @formatter:on

		int id = cursor.getInt(idIndex);
		String webId = cursor.getString(webIdIndex);
		String trackingNumber = cursor.getString(trackingNumberIndex);

		int formId = cursor.getInt(formIdIndex);

		String actionsStringRaw = cursor.getString(actionsIndex);

		String status = cursor.getString(statusIndex);
		int processorId = cursor.getInt(processorIdIndex);
		int authorId = cursor.getInt(authorIdIndex);

		String dateCreated = cursor.getString(dateCreatedIndex);
		String dateUpdated = cursor.getString(dateUpdatedIndex);

		String fieldsEnabledRaw = cursor.getString(fieldsEnabledIndex);
		String fieldsRequiredRaw = cursor.getString(fieldsRequiredIndex);
		String fieldsHiddenRaw = cursor.getString(fieldsHiddenIndex);

		String fieldValuesJSONString = cursor.getString(fieldsValuesIndex);
		int isStarredInt = cursor.getInt(isStarredIndex);
		StarMark starMark = isStarredInt == 1 ? StarMark.STARRED : StarMark.UNSTARRED;

		String commentsLastUpdateDate = cursor.getString(commentsLastUpdateDateIndex);

		// Throws JSONException
		JSONArray actionsRaw = new JSONArray(actionsStringRaw);
		// Throws JSONException
		List<DocumentAction> actions = DocumentActionJSONParser.createFromJSON(actionsRaw);

		List<String> fieldsEnabled = Serializer.unserializeList(fieldsEnabledRaw);
		List<String> fieldsRequired = Serializer.unserializeList(fieldsRequiredRaw);
		List<String> fieldsHidden = Serializer.unserializeList(fieldsHiddenRaw);

		JSONObject fieldValuesJSON = new JSONObject(fieldValuesJSONString);

		Document document = new Document();

		document.setId(id);
		document.setWebId(webId);
		document.setTrackingNumber(trackingNumber);

		document.setFormId(formId);

		document.setActions(actions);

		document.setStatus(status);
		document.setProcessorId(processorId);
		document.setAuthorId(authorId);

		document.setDateCreated(dateCreated);
		document.setDateUpdated(dateUpdated);

		document.setFieldsEnabled(fieldsEnabled);
		document.setFieldsRequired(fieldsRequired);
		document.setFieldsHidden(fieldsHidden);

		document.setFieldValuesJSON(fieldValuesJSON);
		document.setStarMark(starMark);

		document.setCommentsLastUpdateDate(commentsLastUpdateDate);

		return document;

	}

	public static DocumentSummary cursorToDocumentSummary(Cursor cursor) throws JSONException {

		// @formatter:off
		// Initialize Indices
		int documentIdIndex 		= cursor.getColumnIndexOrThrow("document_id");
		int trackingNumberIndex		= cursor.getColumnIndexOrThrow("tracking_number");
		int statusIndex 			= cursor.getColumnIndexOrThrow("status");
		int formIdIndex				= cursor.getColumnIndexOrThrow("form_id");
		int formNameIndex 			= cursor.getColumnIndexOrThrow("form_name");
		int authorIdIndex 			= cursor.getColumnIndexOrThrow("author_id");
		int processorIdIndex 		= cursor.getColumnIndexOrThrow("processor_id");
		int authorDisplayNameIndex 	= cursor.getColumnIndexOrThrow("author_display_name");
		int isStarredIndex 			= cursor.getColumnIndexOrThrow("is_starred");
		int actionsIndex 			= cursor.getColumnIndexOrThrow("actions");
		int fieldValuesIndex 		= cursor.getColumnIndexOrThrow("field_values");
		int dateUpdatedIndex 		= cursor.getColumnIndexOrThrow("date_updated");
		int commentCountIndex 		= cursor.getColumnIndexOrThrow("comment_count");		
		
		// Get Values
		int documentId 				= cursor.getInt(documentIdIndex);
		String trackingNumber		= cursor.getString(trackingNumberIndex);
		String status 				= cursor.getString(statusIndex);
		int formId					= cursor.getInt(formIdIndex);
		String formName 			= cursor.getString(formNameIndex);
		int authorId 				= cursor.getInt(authorIdIndex);
		int processorId 			= cursor.getInt(processorIdIndex);
		String authorDisplayName 	= cursor.getString(authorDisplayNameIndex);
		String dateUpdatedString 	= cursor.getString(dateUpdatedIndex);
		int commentCount 			= cursor.getInt(commentCountIndex);
		
		int starMarkInt 	= cursor.getInt(isStarredIndex);
		StarMark starMark 	= starMarkInt == 1 ? StarMark.STARRED : StarMark.UNSTARRED;

		String actionsString 		= cursor.getString(actionsIndex);
		JSONArray actionsJSONArray 	= new JSONArray(actionsString);
		List<DocumentAction> documentActions = DocumentActionJSONParser
		        .createFromJSON(actionsJSONArray);

		String fieldValuesString 			= cursor.getString(fieldValuesIndex);
		JSONObject fieldValuesJSONObject 	= new JSONObject(fieldValuesString);
		// @formatter:on

		DocumentSummary summary = new DocumentSummary();

		summary.setDocumentId(documentId);
		summary.setTrackingNumber(trackingNumber);
		summary.setStatus(status);
		summary.setFormId(formId);
		summary.setFormName(formName);
		summary.setAuthorId(authorId);
		summary.setProcessorId(processorId);
		summary.setAuthorDisplayName(authorDisplayName);

		summary.setStarMark(starMark);
		summary.setDocumentActions(documentActions);
		summary.setFieldValuesJSON(fieldValuesJSONObject);

		summary.setDateUpdatedString(dateUpdatedString);
		summary.setCommentCount(commentCount);

		return summary;
	}

	public static JSONObject cursorToJSONObject(Cursor cursor) throws JSONException {

		JSONObject json = new JSONObject();

		int columnCount = cursor.getColumnCount();
		for (int i = 0; i < columnCount; i++) {

			String fieldName = cursor.getColumnName(i);
			String fieldValue = cursor.getString(i);

			if (fieldValue != null) {
				json.put(fieldName, fieldValue);
			} else {
				json.put(fieldName, "");
			}

		}

		return json;

	}
	// }}
}
